feat(sdk): loading state — scene image, frosted card, animated mark#64
Open
LEVI-RIVKIN wants to merge 1 commit into
Open
feat(sdk): loading state — scene image, frosted card, animated mark#64LEVI-RIVKIN wants to merge 1 commit into
LEVI-RIVKIN wants to merge 1 commit into
Conversation
Contributor
📦 Snapshot ReleasePublished snapshot packages for this PR: pnpm add @perspective-ai/sdk@1.13.0-pr-64-20260706211041
pnpm add @perspective-ai/sdk-react@1.13.0-pr-64-20260706211041Or use the npm tag (always points to the latest snapshot from this PR): pnpm add @perspective-ai/sdk@pr-64
pnpm add @perspective-ai/sdk-react@pr-64 |
There was a problem hiding this comment.
Pull request overview
This PR updates the SDK’s embed loading skeleton to be a neutral, container-query–responsive overlay that reflects only the iframe’s outer “shape” (boxed on wide slots, full-bleed on narrow slots), with a calmer single-bar shimmer animation and updated default background colors aligned to the interview app’s surface tokens.
Changes:
- Replaced the detailed, UI-mirroring skeleton DOM with a minimal structure (content/surface + single shimmer bar) and moved layout/animation to a shared injected stylesheet driven by container queries.
- Updated default light/dark background and shimmer/border translucency values to match the app’s interview surface and reduce visual flash at handoff.
- Rewrote unit tests to assert the new structure, shared-style injection, container-query usage, and reduced-motion behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/sdk/src/loading.ts | Reworks skeleton implementation to a generic, container-query–responsive overlay with a shared injected stylesheet and per-instance CSS variables. |
| packages/sdk/src/loading.test.ts | Updates tests to validate the new DOM structure, stylesheet injection, and animation/scoping behavior. |
| .changeset/generic-loading-skeleton.md | Adds a patch changeset describing the new neutral skeleton behavior and compatibility notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1c8f3c9 to
3e4fbcb
Compare
0361841 to
7d90c82
Compare
Replace the embed loading skeleton with the interview's real chrome, rendered
while the iframe loads:
- The research's scene image (from {host}/interview/{researchId}/scene-image)
paints full-bleed behind the card, applied only after the image actually
loads and layered over the app's default surface color (#f5f2f0 light,
#15171e dark, or a custom brand.bg) — a research without a scene (404), a
slow network, or a blocked request degrades to a clean solid color.
- A frosted translucent card (55% panel + backdrop blur, matching the app's
card treatment) centers the animated Perspective mark (30px) — the app's
LogoSymbol geometry and timing, driven by CSS keyframes rather than SMIL:
dynamically-inserted SMIL is unreliable in Chromium, and as essential
loading motion the mark is not disabled by prefers-reduced-motion (an OS
animation toggle would otherwise leave a static logo that reads as broken).
- The loading state lives in the host page DOM (overlay sibling of the
cross-origin iframe), so responsiveness is a CSS container query on the
consumer's slot — not a viewport media query. At the app's own 672px
breakpoint the card switches between a centered box (desktop) and
full-bleed (mobile / slider / popup / float); the vertical inset uses
percentage inset on the absolutely-positioned wrapper so it tracks the
slot's height.
- New prefetchSceneImage(researchId, host?) warms the scene on intent
signals — wired to float-bubble hover/focus and to data-perspective-popup /
-slider trigger elements — so deferred embeds open with the scene cached.
Requests are deduped page-wide; widget/fullpage already fetch at mount, in
parallel with the iframe.
- LoadingOptions gains researchId/host (all embed types pass them);
appearance is kept as a deprecated, ignored field for TS compatibility.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kJptBdnAQ2VfzrE9ob1Wb
7d90c82 to
ab32b62
Compare
vinaypuppal
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the embed loading skeleton with the interview's real chrome, rendered while the iframe loads:
{host}/interview/{researchId}/scene-image— painted full-bleed behind the card. Applied only after the image actually loads, layered over the app's default surface color (#f5f2f0light /#15171edark / custombrand.bg), so a research with no scene (404), a slow network, or a blocked request degrades to a clean solid color.backdrop-filter: blur(22px), matching the interview app's card treatment).prefers-reduced-motion(an OS-level animation toggle would otherwise leave a static logo that reads as broken).prefetchSceneImage(researchId, host?)export warms the scene image before deferred embeds open — wired to float-bubble hover/focus and todata-perspective-popup/data-perspective-slidertrigger elements. Requests are deduped page-wide; widget/fullpage already fetch at mount, in parallel with the iframe.Why
The previous skeleton reproduced the interview's welcome card, message lines, and input pill — each a moving target that drifted out of sync with the app. This version renders only stable, brand-owned elements: the scene the research actually uses, the card treatment, and the logo animation, all sourced from the app itself.
Layout
The loading state lives in the host page DOM (overlay sibling of the cross-origin iframe), so responsiveness is a CSS container query on the consumer's slot — not a viewport media query. At the app's own
672pxbreakpoint the card switches between a centered box (desktop) and full-bleed (mobile / slider / popup / float); the vertical inset uses percentageinseton the absolutely-positioned wrapper so it tracks the slot's height.API
LoadingOptionsgains optionalresearchIdandhost(used to build the scene URL); all five embed types pass them automatically.prefetchSceneImage(researchId, host?)is exported for consumers who want to warm the scene themselves.LoadingOptions.appearance(hideBranding/hideProgress/hideGreeting) is kept as a deprecated, ignored field so existing TypeScript callers keep compiling — no breaking changes.Testing
packages/sdkunit suite: 418 passed (loading tests rewritten: structure, CSS keyframe animation on the LogoSymbol timing with the 0.75s right-side offset and no SMIL nodes, scene URL construction/encoding, prefetch dedup — including float-bubble hover triggering it — color fallbacks, container-query scoping, deprecatedappearanceaccepted-and-ignored).prefers-reduced-motion: reduceemulated.404+max-age=60when no scene is set).Changeset
@perspective-ai/sdk— minor (new loading design; additiveLoadingOptionsfields andprefetchSceneImageexport).🤖 Generated with Claude Code